asset manager

安装量: 69
排名: #11137

安装

npx skills add https://github.com/daffy0208/ai-dev-standards --skill 'Asset Manager'

Asset Manager Keep design assets organized, optimized, and accessible. Core Principle Organized assets = faster development. Assets should be: Easy to find Properly named Optimized for production Version controlled Consistently formatted Phase 1: Asset Organization Directory Structure assets/ ├── images/ │ ├── products/ │ ├── team/ │ ├── marketing/ │ └── ui/ ├── icons/ │ ├── svg/ │ └── png/ ├── fonts/ │ ├── primary/ │ └── secondary/ ├── videos/ ├── logos/ │ ├── svg/ │ ├── png/ │ └── variants/ └── brand/ ├── colors.json ├── typography.json └── guidelines.pdf Naming Conventions Images: {category}-{description}-{size}.{format} Examples: product-hero-1920x1080.jpg team-sarah-400x400.jpg ui-background-pattern.png Icons: {icon-name}-{variant}.svg Examples: home-outline.svg home-filled.svg user-circle.svg arrow-right.svg Fonts: {font-family}-{weight}.{format} Examples: Inter-Regular.woff2 Inter-Bold.woff2 Poppins-SemiBold.woff2 Automated Organization Script // scripts/organize-assets.ts import fs from 'fs/promises' import path from 'path' interface AssetRule { pattern : RegExp destination : string } const rules : AssetRule [ ] = [ { pattern : / product- / i , destination : 'images/products' } , { pattern : / team- / i , destination : 'images/team' } , { pattern : / icon- / i , destination : 'icons/svg' } , { pattern : / logo- / i , destination : 'logos' } ] async function organizeAssets ( sourceDir : string ) { const files = await fs . readdir ( sourceDir ) for ( const file of files ) { const sourcePath = path . join ( sourceDir , file ) const stat = await fs . stat ( sourcePath ) if ( stat . isDirectory ( ) ) continue // Find matching rule const rule = rules . find ( r => r . pattern . test ( file ) ) if ( rule ) { const destDir = path . join ( 'assets' , rule . destination ) await fs . mkdir ( destDir , { recursive : true } ) const destPath = path . join ( destDir , file ) await fs . rename ( sourcePath , destPath ) console . log ( Moved: ${ file } → ${ rule . destination } ) } } console . log ( 'Assets organized!' ) } organizeAssets ( './unsorted-assets' ) Phase 2: Image Optimization Install Optimization Tools npm install sharp imagemin imagemin-mozjpeg imagemin-pngquant imagemin-svgo Optimize Images Script // scripts/optimize-images.ts import sharp from 'sharp' import imagemin from 'imagemin' import imageminMozjpeg from 'imagemin-mozjpeg' import imageminPngquant from 'imagemin-pngquant' import imageminSvgo from 'imagemin-svgo' import fs from 'fs/promises' import path from 'path' interface OptimizeOptions { quality ? : number maxWidth ? : number formats ? : ( 'jpg' | 'png' | 'webp' | 'avif' ) [ ] } async function optimizeImages ( inputDir : string , outputDir : string , options : OptimizeOptions = { } ) { const { quality = 80 , maxWidth = 2000 , formats = [ 'jpg' , 'png' , 'webp' ] } = options const files = await fs . readdir ( inputDir ) for ( const file of files ) { const inputPath = path . join ( inputDir , file ) const stat = await fs . stat ( inputPath ) if ( stat . isDirectory ( ) ) continue const ext = path . extname ( file ) . toLowerCase ( ) const name = path . basename ( file , ext ) console . log ( Processing: ${ file } ) // Skip SVGs (handle separately) if ( ext === '.svg' ) { await optimizeSVG ( inputPath , outputDir ) continue } // Skip non-images if ( ! [ '.jpg' , '.jpeg' , '.png' ] . includes ( ext ) ) continue // Read image const image = sharp ( inputPath ) const metadata = await image . metadata ( ) // Resize if too large if ( metadata . width && metadata . width

maxWidth ) { image . resize ( maxWidth , null , { withoutEnlargement : true , fit : 'inside' } ) } // Generate formats for ( const format of formats ) { const outputPath = path . join ( outputDir , ${ name } . ${ format } ) if ( format === 'jpg' ) { await image . jpeg ( { quality , mozjpeg : true } ) . toFile ( outputPath ) } else if ( format === 'png' ) { await image . png ( { quality , compressionLevel : 9 } ) . toFile ( outputPath ) } else if ( format === 'webp' ) { await image . webp ( { quality } ) . toFile ( outputPath ) } else if ( format === 'avif' ) { await image . avif ( { quality } ) . toFile ( outputPath ) } console . log ( ✓ Generated ${ format } ) } } console . log ( 'Images optimized!' ) } async function optimizeSVG ( inputPath : string , outputDir : string ) { const fileName = path . basename ( inputPath ) const outputPath = path . join ( outputDir , fileName ) await imagemin ( [ inputPath ] , { destination : outputDir , plugins : [ imageminSvgo ( { plugins : [ { name : 'removeViewBox' , active : false } , { name : 'removeDimensions' , active : true } , { name : 'removeUselessStrokeAndFill' , active : true } ] } ) ] } ) console . log ( ✓ Optimized SVG ) } // Usage optimizeImages ( './assets/images/raw' , './assets/images/optimized' , { quality : 85 , maxWidth : 1920 , formats : [ 'jpg' , 'webp' , 'avif' ] } ) Responsive Images Generator // scripts/generate-responsive-images.ts import sharp from 'sharp' import fs from 'fs/promises' import path from 'path' const breakpoints = [ { name : 'mobile' , width : 640 } , { name : 'tablet' , width : 768 } , { name : 'desktop' , width : 1920 } ] async function generateResponsiveImages ( inputPath : string ) { const ext = path . extname ( inputPath ) const name = path . basename ( inputPath , ext ) const dir = path . dirname ( inputPath ) for ( const bp of breakpoints ) { const image = sharp ( inputPath ) // Resize image . resize ( bp . width , null , { withoutEnlargement : true , fit : 'inside' } ) // Generate WebP const webpPath = path . join ( dir , ${ name } - ${ bp . name } .webp ) await image . webp ( { quality : 80 } ) . toFile ( webpPath ) console . log ( ✓ ${ name } - ${ bp . name } .webp ) // Generate AVIF const avifPath = path . join ( dir , ${ name } - ${ bp . name } .avif ) await image . avif ( { quality : 80 } ) . toFile ( avifPath ) console . log ( ✓ ${ name } - ${ bp . name } .avif ) } console . log ( Generated responsive images for: ${ name } ) } // Usage generateResponsiveImages ( './assets/images/hero.jpg' ) Phase 3: Font Management Font Optimization // scripts/optimize-fonts.ts import { exec } from 'child_process' import { promisify } from 'util' import fs from 'fs/promises' import path from 'path' const execAsync = promisify ( exec ) async function optimizeFonts ( inputDir : string , outputDir : string ) { const files = await fs . readdir ( inputDir ) for ( const file of files ) { const inputPath = path . join ( inputDir , file ) const ext = path . extname ( file ) . toLowerCase ( ) if ( ext !== '.ttf' && ext !== '.otf' ) continue const name = path . basename ( file , ext ) console . log ( Processing: ${ file } ) // Convert to WOFF2 (best compression) const woff2Path = path . join ( outputDir , ${ name } .woff2 ) await convertToWOFF2 ( inputPath , woff2Path ) console . log ( ✓ ${ name } .woff2 ) // Convert to WOFF (fallback) const woffPath = path . join ( outputDir , ${ name } .woff ) await convertToWOFF ( inputPath , woffPath ) console . log ( ✓ ${ name } .woff ) } console . log ( 'Fonts optimized!' ) } async function convertToWOFF2 ( input : string , output : string ) { // Requires woff2_compress tool // Install: brew install woff2 await execAsync ( woff2_compress ${ input } ) const woff2File = input . replace ( / . ( ttf | otf ) $ / , '.woff2' ) await fs . rename ( woff2File , output ) } async function convertToWOFF ( input : string , output : string ) { // Requires sfnt2woff tool await execAsync ( sfnt2woff ${ input } ) const woffFile = input . replace ( / . ( ttf | otf ) $ / , '.woff' ) await fs . rename ( woffFile , output ) } optimizeFonts ( './assets/fonts/raw' , './assets/fonts/optimized' ) Font Loading Strategy / fonts.css / / Preload critical fonts / @font-face { font-family : 'Inter' ; src : url ( '/fonts/Inter-Regular.woff2' ) format ( 'woff2' ) , url ( '/fonts/Inter-Regular.woff' ) format ( 'woff' ) ; font-weight : 400 ; font-style : normal ; font-display : swap ; / Show fallback first / } @font-face { font-family : 'Inter' ; src : url ( '/fonts/Inter-Bold.woff2' ) format ( 'woff2' ) , url ( '/fonts/Inter-Bold.woff' ) format ( 'woff' ) ; font-weight : 700 ; font-style : normal ; font-display : swap ; } Preload in HTML: < head

< link rel = " preload " href = " /fonts/Inter-Regular.woff2 " as = " font " type = " font/woff2 " crossorigin /> < link rel = " preload " href = " /fonts/Inter-Bold.woff2 " as = " font " type = " font/woff2 " crossorigin /> </ head

Phase 4: Asset Version Control Git LFS Setup

Install Git LFS

brew install git-lfs git lfs install

Track large files

git lfs track ".psd" git lfs track ".ai" git lfs track ".sketch" git lfs track ".fig" git lfs track ".mp4" git lfs track ".mov" git lfs track "assets/images//*.jpg" git lfs track "assets/images//*.png"

Add .gitattributes

git add .gitattributes git commit -m "Configure Git LFS" Asset Versioning System // scripts/version-assets.ts import fs from 'fs/promises' import path from 'path' import crypto from 'crypto' interface AssetVersion { path : string hash : string size : number modified : string version : number } class AssetVersionManager { private versionFile = 'asset-versions.json' private versions : Map < string , AssetVersion [ ]

= new Map ( ) async load ( ) { try { const data = await fs . readFile ( this . versionFile , 'utf-8' ) const parsed = JSON . parse ( data ) for ( const [ path , versions ] of Object . entries ( parsed ) ) { this . versions . set ( path , versions as AssetVersion [ ] ) } } catch ( error ) { // File doesn't exist yet } } async save ( ) { const data = Object . fromEntries ( this . versions ) await fs . writeFile ( this . versionFile , JSON . stringify ( data , null , 2 ) ) } async trackAsset ( filePath : string ) { const buffer = await fs . readFile ( filePath ) const hash = crypto . createHash ( 'sha256' ) . update ( buffer ) . digest ( 'hex' ) const stat = await fs . stat ( filePath ) const versions = this . versions . get ( filePath ) || [ ] const lastVersion = versions [ versions . length - 1 ] // Check if changed if ( lastVersion && lastVersion . hash === hash ) { console . log ( No changes: ${ filePath } ) return } // Add new version versions . push ( { path : filePath , hash , size : stat . size , modified : stat . mtime . toISOString ( ) , version : versions . length + 1 } ) this . versions . set ( filePath , versions ) console . log ( Tracked: ${ filePath } (v ${ versions . length } ) ) } async trackDirectory ( dirPath : string ) { const files = await fs . readdir ( dirPath , { recursive : true } ) for ( const file of files ) { const filePath = path . join ( dirPath , file . toString ( ) ) const stat = await fs . stat ( filePath ) if ( stat . isDirectory ( ) ) continue await this . trackAsset ( filePath ) } } } // Usage const manager = new AssetVersionManager ( ) await manager . load ( ) await manager . trackDirectory ( './assets' ) await manager . save ( ) Phase 5: Brand Asset Library Brand Kit Structure brand/ ├── logos/ │ ├── primary/ │ │ ├── logo-full.svg │ │ ├── logo-icon.svg │ │ └── logo-wordmark.svg │ ├── variations/ │ │ ├── logo-white.svg │ │ ├── logo-black.svg │ │ └── logo-inverted.svg │ └── exports/ │ ├── png/ │ ├── pdf/ │ └── eps/ ├── colors/ │ ├── colors.json │ ├── colors.css │ └── colors.scss ├── typography/ │ ├── fonts/ │ └── typography.json └── guidelines/ ├── brand-guidelines.pdf ├── logo-usage.pdf └── color-usage.pdf Brand Asset Manifest // brand/manifest.ts export interface BrandAssets { version : string lastUpdated : string logos : LogoAsset [ ] colors : ColorAsset [ ] typography : TypographyAsset [ ] } export interface LogoAsset { name : string variants : { full : string icon : string wordmark : string } formats : { svg : string png : { [ size : string ] : string } pdf : string } } export interface ColorAsset { name : string hex : string rgb : { r : number ; g : number ; b : number } usage : string } export interface TypographyAsset { name : string family : string weights : number [ ] formats : string [ ] } export const brandAssets : BrandAssets = { version : '2.0.0' , lastUpdated : '2024-01-15' , logos : [ { name : 'Primary Logo' , variants : { full : '/brand/logos/logo-full.svg' , icon : '/brand/logos/logo-icon.svg' , wordmark : '/brand/logos/logo-wordmark.svg' } , formats : { svg : '/brand/logos/logo-full.svg' , png : { '1x' : '/brand/logos/exports/png/logo-full@1x.png' , '2x' : '/brand/logos/exports/png/logo-full@2x.png' , '3x' : '/brand/logos/exports/png/logo-full@3x.png' } , pdf : '/brand/logos/exports/pdf/logo-full.pdf' } } ] , colors : [ { name : 'Primary' , hex : '#0066cc' , rgb : { r : 0 , g : 102 , b : 204 } , usage : 'Primary actions, links, brand elements' } ] , typography : [ { name : 'Inter' , family : 'Inter' , weights : [ 400 , 600 , 700 ] , formats : [ 'woff2' , 'woff' ] } ] } Best Practices 1. Optimize for Web Images: Use WebP/AVIF with JPEG fallback Icons: Use SVG sprites for multiple icons Fonts: Use WOFF2 with WOFF fallback Videos: Compress and provide multiple formats 2. Lazy Loading // Lazy load images ; < img src = " placeholder.jpg " data-src = " hero.jpg " loading = " lazy " alt = " Hero " /> // Or use Next.js Image import Image from 'next/image' ; < Image src = " /hero.jpg " width = { 1920 } height = { 1080 } placeholder = " blur " alt = " Hero " /> 3. Asset CDN // Use CDN for assets const CDN_URL = process . env . CDN_URL || '' export function getAssetUrl ( path : string ) : string { if ( CDN_URL ) { return ${ CDN_URL } ${ path } } return path } // Usage < img src = { getAssetUrl ( '/images/hero.jpg' ) } alt = "Hero" /

  1. Automated Asset Pipeline // scripts/asset-pipeline.ts async function runAssetPipeline ( ) { console . log ( 'Starting asset pipeline...' ) // 1. Organize assets await organizeAssets ( './unsorted' ) // 2. Optimize images await optimizeImages ( './assets/images/raw' , './assets/images/optimized' ) // 3. Generate responsive images await generateResponsiveImages ( './assets/images/optimized' ) // 4. Optimize fonts await optimizeFonts ( './assets/fonts/raw' , './assets/fonts/optimized' ) // 5. Version assets const manager = new AssetVersionManager ( ) await manager . load ( ) await manager . trackDirectory ( './assets' ) await manager . save ( ) console . log ( 'Asset pipeline complete!' ) } runAssetPipeline ( ) Tools & Resources Optimization Tools: Sharp
  2. Image processing ImageOptim
  3. Image compression SVGO
  4. SVG optimization Squoosh
  5. Online image compression Font Tools: Glyphhanger
  6. Font subsetting Transfonter
  7. Font conversion FontForge
  8. Font editor
返回排行榜